/**
 * @ Author: luoqi
 * @ Create Time: 2024-11-02 10:16
 * @ Modified by: Your name
 * @ Modified time: 2025-08-11 22:59:06
 * @ Description:
 */

#include "qfsm.h"

#define QFSM_ENTRY(ptr)     ((QFsmState *)((char *)(ptr) - ((unsigned long) &((QFsmState*)0)->_node_)))
#define QFSM_ITERATOR(node, list)     for(node = (list)->next; node != (list); node = node->next)
#define QFSM_ITERATOR_SAFE(node, safe, list)      for(node = (list)->next, safe = node->next; node != (list); node = safe, safe = node->next)

static uint32_t _id_calc(const char *name)
{
    if(!name) {
        return 0;
    }
    uint32_t hash = 5381;
    int c;
    while((c = *name++)) {
        hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
    }
    return hash;
}

static inline void _list_insert(QFsmList *list, QFsmList *node)
{
    if(!list || !node) {
        return;
    }
    list->next->prev = node;
    node->next = list->next;

    list->next = node;
    node->prev = list;
}

static inline void _list_remove(QFsmList *node)
{
    if(!node) {
        return;
    }
    node->next->prev = node->prev;
    node->prev->next = node->next;

    node->next = node->prev = node;
}

static int _state_isexist(QFsm *fsm, QFsmState *state)
{
    if(!fsm || !state) {
        return -1;
    }
    QFsmState *_state;
    QFsmList *node, *safe;

    QFSM_ITERATOR_SAFE(node, safe, &fsm->_list_)
    {
        _state = QFSM_ENTRY(node);
        if(_state->id == state->id) {
            return 1;
        } else {
            continue;
        }
    }
    return 0;
}

int qfsm_init(QFsm *fsm)
{
    if(!fsm) {
        return -1;
    }
    fsm->current = NULL;
    fsm->hook = NULL;
    fsm->param = NULL;
    fsm->_list_.prev = &fsm->_list_;
    fsm->_list_.next = &fsm->_list_;
    return 0;
}

int qfsm_setup(QFsm *fsm, const char *start_state_name, void *param)
{
    if(!fsm) {
        return -1;
    }
    fsm->param = param;
    uint32_t id = _id_calc(start_state_name);

    QFsmState *state;
    QFsmList *node, *safe;

    int found = 0;
    QFSM_ITERATOR_SAFE(node, safe, &fsm->_list_)
    {
        state = QFSM_ENTRY(node);
        if(state->id == id) {
            fsm->current = state;
            found = 1;
            break;
        }
    }
    if(!found) {
        return -1;
    }

    QFSM_ITERATOR_SAFE(node, safe, &fsm->_list_)
    {
        state = QFSM_ENTRY(node);
        if(state->next) {
            continue;
        }
        QFsmList *inner_node;
        QFSM_ITERATOR(inner_node, &fsm->_list_)
        {
            QFsmState *candidate = QFSM_ENTRY(inner_node);
            if(state->next_id == candidate->id) {
                state->next = candidate;
                break;
            }
        }
    }
    return 0;
}

int qfsm_exec(QFsm *fsm)
{
    if(!fsm || !fsm->current) {
        return -1;
    }
    QFsmState *current = fsm->current;
    int ret = current->cb(fsm->param);
    if(fsm->current != current) {
        return 0;
    }
    if(ret == QFSM_NEXT) {
        if(fsm->hook) {
            fsm->hook(fsm->param);
        }
        if(current->out) {
            current->out(fsm->param);
            if(fsm->current != current) {
                return 0;
            }
        }
        if(!current->next) {
            return -1;
        }
        QFsmState *next = current->next;
        fsm->current = next;
        if(next->in) {
            next->in(fsm->param);
        }
    }
    return 0;
}

int qfsm_hook_set(QFsm *fsm, QFsmHook hook)
{
    if(!fsm || !hook) {
        return -1;
    } else {
        fsm->hook = hook;
    }
    return 0;
}

int qfsm_add(QFsm *fsm, QFsmState *state, const char *name, const char *next_name, QFsmCallback cb, QFsmHook in, QFsmHook out)
{
    if(!fsm && !state && !cb && !in && !out) {
        return -1;
    }
    state->name = name;
    state->cb = cb;
    state->in = in;
    state->out = out;
    state->id = _id_calc(name);
    state->next_id = _id_calc(next_name);
    if(_state_isexist(fsm, state)) {
        return -1;
    } else {
        _list_insert(&fsm->_list_, &state->_node_);
    }
    return 0;
}

int qfsm_del(QFsm *fsm, QFsmState *state)
{
    if(!fsm || !state) {
        return -1;
    } else if(_state_isexist(fsm, state)) {
        _list_remove(&state->_node_);
    } else {
        return -1;
    }
    return 0;
}

int qfsm_skip(QFsm *fsm, const char *state_name)
{
    if(!fsm || !state_name || !fsm->current) {
        return -1;
    }
    
    if(fsm->inhook) {
        return -1;
    }

    uint32_t id = _id_calc(state_name);
    QFsmState *skip_to = NULL;
    QFsmList *node, *safe;
    QFSM_ITERATOR_SAFE(node, safe, &fsm->_list_)
    {
        QFsmState *state = QFSM_ENTRY(node);
        if((state->id == id) && (fsm->current->id != id)) {
            skip_to = state;
            break;
        }
    }
    if(!skip_to) {
        return -1;
    }
    QFsmState *current = fsm->current;

    if(current->out) {
        fsm->inhook = true;
        current->out(fsm->param);
        fsm->inhook = false;
        if(fsm->current != current) {
            return 0;
        }
    }

    if(fsm->hook) {
        fsm->hook(fsm->param);
    }

    fsm->current = skip_to;
    if(skip_to->in) {
        skip_to->in(fsm->param);
    }
    return 0;
}

int qfsm_dump(QFsm *fsm, int (*dump)(const char *fmt, ...))
{
    if(!fsm || !dump) {
        return -1;
    }
    dump("FSM states:\r\n");
    size_t count = 0;
    QFsmList *node, *safe;
    QFSM_ITERATOR_SAFE(node, safe, &fsm->_list_)
    {
        count++;
        QFsmState *state = QFSM_ENTRY(node);
        dump(" -name: %-10s| next: %-10s\r\n", state->name, state->next->name);
    }
    dump(" -state num: %u\r\n", count);
    dump(" -current: %s\r\n", fsm->current->name);
    return 0;
}
